#ifndef _TIMER_H_
#define _TIMER_H_

#include "util.h"

struct ThreadTimer;

const int64 kTimerNumSpots = 10;
const int64 kTimerSpacing = 8;

// there should only be one of these. It keeps a thread busy. 
struct TimerThread {
  TimerThread();
  void Launch();
  void SetTimes();
  void Run() { while(1) SetTimes(); }
  int64 Get(int which) {
    if (which == -1) 
      return TimeNS();
    return time_[which * kTimerSpacing]; 
  }
  int32 GetID() { 
    if (!running_) return -1;
    int32 ret = next_id_; 
    next_id_ = (next_id_ + 1) % kTimerNumSpots;
    return ret;
  }
  volatile int64 time_[kTimerSpacing * kTimerNumSpots];
  int32 next_id_;
  bool running_;
};

extern TimerThread G_TIMER_THREAD;

inline int64 GTime(int32 id) { return G_TIMER_THREAD.Get(id); }

// all times are in nanoseconds. 
struct Clock {
  Clock() { 
    timer_id_ = G_TIMER_THREAD.GetID();
    paused_at_ = -1; 
    Set(0); 
  }
  void NonPausedSet(int64 t) {
    offset_ = t - GTime(timer_id_);
  }
  void Set(int64 t) {
    CHECK(t < 1000000000000000000ll);
    if (IsPaused()) paused_at_ = t;
    else NonPausedSet(t);
  }
  void Advance(int64 t) { 
    if (IsPaused()) paused_at_ += t;
    else offset_ += t;
  }
  int64 Get() { 
    if (paused_at_ != -1) return paused_at_;
    int64 gtime = GTime(timer_id_);
    int64 offset = offset_;
    return gtime + offset;
  }
  bool IsPaused() {
    return (paused_at_ != -1);
  }
  void Pause() {
    CHECK(!IsPaused());
    paused_at_ = Get();    
  }
  void Unpause() {
    CHECK(IsPaused());
    NonPausedSet(paused_at_);
    paused_at_ = -1;
  }
  struct Pauser {
    Pauser(Clock *clock) :clock_(clock) {clock_->Pause();}
    ~Pauser() {clock_->Unpause();}
    Clock *clock_;
  };
  int32 timer_id_;
  int64 offset_;
  int64 paused_at_;
};

void InitTimer();

void TimeTest();

#endif
